package com.example.gfile.util;

import com.example.gfile.exception.GFileException;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;


public class ZipUtil {


    /**
     * 压缩文件
     *
     * @param sourceFiles 原文件数据集
     * @param zipFile     压缩后的文件地址
     */
    public static String doZip(List<String> sourceFiles, String zipFile) {
        File zip = new File(zipFile);
        if (!zip.getParentFile().exists()) {
            zip.getParentFile().mkdirs();
        }
        try (ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(zip)) {
            // Zip64Mode 枚举有 3 个值：
            // Always：对所有条目使用 Zip64 扩展、
            // Never：不对任何条目使用Zip64扩展、
            // AsNeeded：对需要的所有条目使用Zip64扩展
            outputStream.setUseZip64(Zip64Mode.AsNeeded);
            for (String sourceFile : sourceFiles) {
                File file = new File(sourceFile);
                //将每个源文件用 ZipArchiveEntry 实体封装，然后添加到压缩文件中. 这样将来解压后里面的文件名称还是保持一致.
                ZipArchiveEntry entry = new ZipArchiveEntry(file.getName());
                // 可以设置压缩等级
                outputStream.setLevel(5);
                // 可以设置压缩算法，当前支持ZipEntry.DEFLATED和ZipEntry.STORED两种
                outputStream.setMethod(ZipEntry.DEFLATED);
                // 也可以为每个文件设置压缩算法
                entry.setMethod(ZipEntry.DEFLATED);
                // 在zip中创建一个文件
                outputStream.putArchiveEntry(entry);
                try (InputStream inputStream = new FileInputStream(file)) {
                    byte[] buffer = new byte[1024 * 5];
                    int length = -1;//每次读取的字节大小。
                    while ((length = inputStream.read(buffer)) != -1) {
                        //把缓冲区的字节写入到 ZipArchiveEntry
                        outputStream.write(buffer, 0, length);
                    }
                    // 完成一个文件的写入
                } catch (FileNotFoundException e) {
                    throw new GFileException(e.getMessage(), e);
                }
            }
            outputStream.closeArchiveEntry(); //写入此条目的所有必要数据。如果条目未压缩或压缩后的大小超过4 GB 则抛出异常
            outputStream.finish();//压缩结束.
        } catch (Exception e) {
            throw new GFileException(e.getMessage(), e);
        }
        return zipFile;
    }

    /**
     * 解压zip
     *
     * @param zipSource      zip文件路径 必传
     * @param destPath 如果为空那么解压到zipPath的当前目录,不为空解压到指定目录
     */
    public static void unZip(String zipSource, String destPath) {
        try {
            if (StringUtils.isNotBlank(destPath)) {
                destPath = new File(destPath).getAbsolutePath();
            } else {
                destPath = new File(zipSource).getParent();
            }
            File zip = new File(zipSource);
            ZipFile zipFile = new ZipFile(zip);
            byte[] buffer = new byte[4096];
            ZipArchiveEntry entry;
            Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); // 获取全部文件的迭代器

            while (entries.hasMoreElements()) {
                entry = entries.nextElement();
                if (entry.isDirectory()) {
                    continue;
                }

                File outputFile = new File(destPath + entry.getName());

                if (!outputFile.getParentFile().exists()) {
                    outputFile.getParentFile().mkdirs();
                }

                try (InputStream inputStream = zipFile.getInputStream(entry);
                     FileOutputStream fos = new FileOutputStream(outputFile)) {
                    while (inputStream.read(buffer) > 0) {
                        fos.write(buffer);
                    }
                } catch (IOException e) {
                    throw new GFileException(e.getMessage(), e);
                }
            }
        } catch (IOException e) {
            throw new GFileException(e.getMessage(), e);
        }
    }
}
